<?php


namespace App\Utils;

use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use Hyperf\HttpMessage\Upload\UploadedFile;

/**
 * 文件上传
 * Class Upload
 * @package App\Utils
 */
class Upload
{
    protected $path = '/tmp';

    //后缀
    protected $allowSuffix = [];

    //mime类型
    protected $allowMime = [];

    //允许上传的大小
    protected $maxSize = 2 * 1024 * 1024;

    //文件后缀
    protected $postfix = '';


    /**文件信息*/

    //文件名
    protected $oldName;

    //文件后缀
    protected $suffix;

    //文件大小
    protected $size;

    //文件mime
    protected $mime;

    //临时文件路径
    protected $tmpName;

    //文件新名称
    protected $newName;

    protected $rootPath = '/';

    /**
     * 构造方法
     * Upload constructor.
     * @param array $config
     */
    public function __construct($config = [])
    {
        foreach ($config as $k => $v) {
            $this->setOption($k, $v);
        }

        $this->rootPath = config('server.settings.document_root');
    }

    /**
     * 判断key是不是成员属性，如果是就设置
     * @param $key
     * @param $value
     */
    protected function setOption($key, $value)
    {
        //获取所有的成员属性
        $keys = array_keys(get_class_vars(__CLASS__));
        if (in_array($key, $keys)) {
            $this->$key = $value;
        }
    }

    public function uploadFile($file)
    {
        //传输文件不符合要求
        if (!($file instanceof UploadedFile)) {
            throw new BusinessException(ErrorCode::UPLOAD_ERROR);
        }


        //判断有没有设置路径path
        if (empty($this->path)) {
            throw new BusinessException(ErrorCode::UPLOAD_PATH_EMPTY);
        }

        //判断该路径是否存在是否可写
        if (!$this->check()) {
            throw new BusinessException(ErrorCode::UPLOAD_PATH_NOT_AUTH);
        }

        $this->getFileInfo($file);

        //判断文件大小，mime，后缀是否符合
        $this->checkSize();
        $this->checkSuffix();
        $this->checkMime();


        //新名称
        $this->newName = $this->createNewName();

        //判断是否是上传文件，并且移动文件
        if ($file->isValid()) {
            $path = "{$this->path}/{$this->newName}";
            $pathReal = $this->rootPath . $path;
            $file->moveTo($pathReal);
            if ($file->isMoved()) {
                return $path;
            } else {
                throw new BusinessException(ErrorCode::UPLOAD_MOVE_ERROR);
            }
        } else {
            throw new BusinessException(ErrorCode::UPLOAD_NOT_UPLOAD_FILE);
        }
    }


    /**
     * 检测文件夹是否存在，是否可写
     * @return bool
     */
    protected function check()
    {
        $pathReal = $this->rootPath . $this->path;
        //文件夹不存在或者不是目录，创建文件夹
        if (!file_exists($pathReal) || !is_dir($pathReal)) {
            return mkdir($pathReal, 0777, true);
        }

        //判断文件是否可写
        if (!is_writeable($pathReal)) {
            return chmod($pathReal, 0777);
        }
        return true;
    }

    protected function getFileInfo(UploadedFile $file)
    {
        $this->oldName = $file->getClientFilename();
        $this->mime = $file->getMimeType();
        $this->tmpName = $file->getPathname();
        $this->size = $file->getSize();
        $this->suffix = $file->getExtension();
    }

    /**
     * 判断文件大小
     */
    protected function checkSize()
    {
        if ($this->size > $this->maxSize) {
            throw new BusinessException(ErrorCode::UPLOAD_SIZE_LIMIT);
        }
    }

    /**
     * 判断mime类型
     */
    protected function checkMime()
    {
        if (count($this->allowMime) > 0 && !in_array($this->mime, $this->allowMime)) {
            throw new BusinessException(ErrorCode::UPLOAD_MIME_LIMIT);
        }
    }

    /**
     * 判断后缀
     */
    protected function checkSuffix()
    {
        if (count($this->allowSuffix) > 0 && !in_array($this->suffix, $this->allowSuffix)) {
            throw new BusinessException(ErrorCode::UPLOAD_SUFFIX_LIMIT);
        }
    }

    /**
     * 创建新名称
     * @return string
     */
    protected function createNewName()
    {
        //TODO 可能还会添加文件判断，防止同文件多次上传

        return md5_file($this->tmpName) . $this->postfix . '.' . $this->suffix;
    }

}